{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "dda97799",
   "metadata": {},
   "source": [
    "# 利用 LOCC 来进行两方量子态分辨\n",
    "\n",
    "\n",
    "<em> Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. </em>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4942c442",
   "metadata": {},
   "source": [
    "## 概述\n",
    "\n",
    "量子态分辨（quantum state discrimination, QSD）[1-2] 是量子通信，量子计算和量子密码学中的一个基本问题。本教程展示了如何通过本地量子操作和经典通信（LOCC）来区分满足 $\\langle\\psi\\lvert\\phi\\rangle=0$ 的两个正交的两方纯态 $\\lvert\\psi\\rangle$ 和 $\\lvert\\phi\\rangle$。本教程中使用的方法和理论参考了 [3] 。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e7347c1f",
   "metadata": {},
   "source": [
    "## 寻找一个态分辨协议\n",
    "\n",
    "首先，我们将需要处理的问题定义清楚。考虑两个在空间上相隔一定距离的参与方 $ A $（Alice）和 $ B $（Bob）共享一对两个量子比特系统，该系统的状态 $|\\varphi\\rangle$ 由第三方 $C$（Charlie）提前制备好分发给参与方。Alice 和 Bob 只是被提前通知了 $|\\varphi\\rangle$ 可能是 $\\lvert\\psi\\rangle$ 和 $\\lvert\\phi\\rangle$ 两者其中之一，并且满足条件 $\\langle\\psi\\lvert\\phi\\rangle=0$。然后，Charlie 给参与方提供了很多 $\\lvert\\psi\\rangle$ 和 $\\lvert\\phi\\rangle$ 的拷贝并希望 Alice 和 Bob 可以通过这些来正确的分辨出他们现在持有的量子态究竟是 $\\lvert\\psi\\rangle$ 和 $\\lvert\\phi\\rangle$ 中的哪一个。\n",
    "\n",
    "在我们提供的 LOCCNet 框架下解决这样一个任务是很轻松的。方便起见，我们还是先采用单轮通讯协议 $r=1$ 的设计。 也就是说 Alice 和 Bob 之间只会进行一次经典通讯。具体的量子神经网络（QNN）见图 1。该任务中的关键步骤是确定损失函数 $ L $。这里我们设置 Alice 和 Bob 都需要对手中量子比特做一次测量，因此有 4 种可能的测量结果 $m_Am_B\\in\\{00, 01, 10, 11\\}$。为了能清晰地区分 $\\lvert\\psi\\rangle$ 和 $\\lvert\\phi\\rangle$， 我们不妨定义当测量结果为 $m_Am_B\\in\\{00, 10\\}$ 时将其分类为 $\\lvert\\psi\\rangle$。 类似地，如果参与方的测量结果为 $m_Am_B\\in\\{01, 11\\}$ 时将其分类为 $\\lvert\\phi\\rangle$。此步骤可以理解为在监督学习中为数据添加标签。通过以上标签，我们可以通过分辨协议出错的概率来定义损失函数，\n",
    "\n",
    "$$\n",
    "L = p_{\\lvert\\psi\\rangle\\_01}+p_{\\lvert\\psi\\rangle\\_11}+p_{\\lvert\\phi\\rangle\\_10}+p_{\\lvert\\phi\\rangle\\_00}. \n",
    "\\tag{1}\n",
    "$$\n",
    "\n",
    "其中 $p_{\\lvert\\psi\\rangle\\_01}$ 代表输入态为 $\\lvert\\psi\\rangle$ 时测量 01 的概率（**注释：** 这是一种分类错误的情况）。接着我们可以通过优化算法来最小化损失函数。\n",
    "\n",
    "![](figures/discrimination-fig-circuit.png \"图 1. 用 LOCCNet 进行两方态分辨的协议示意图\")\n",
    "<div style=\"text-align:center\">图 1. 用 LOCCNet 进行两方态分辨的协议示意图 </div>\n",
    "\n",
    "我们将整个流程进行如下总结：\n",
    "\n",
    "1. Alice 和 Bob 各自拥有一个二量子比特系统的一部分，整个系统的量子态可能为 $\\lvert\\psi\\rangle$ 或者 $\\lvert\\phi\\rangle$。这两个态正交且均是纯态。\n",
    "2. Alice 对自己的量子比特进行酉变换 $U_A$（单比特通用门）。\n",
    "3. Alice 在计算基（computational basis）上测量其量子比特，测量结果 $m_A\\in \\{0, 1\\}$。然后，她通过经典信道告知 Bob 自己的测量结果。\n",
    "4. Bob 根据 Alice 的测量结果在自己量子比特上施加不同的门。如果 $ m_A = 0 $， Bob 对其持有的量子比特作用 $ U_{B0} $；如果 $ m_A = 1 $，则 Bob 施加 $ U_{B1} $。然后 Bob 测量自己的量子比特得到结果 $m_B \\in \\{0,1\\}$。**注意**：这里 $ U_{B0} $ 和 $ U_{B1} $ 都是单比特上的广义旋转门。\n",
    "5. 计算损失函数 $L = p_{\\lvert\\psi\\rangle\\_01}+p_{\\lvert\\psi\\rangle\\_11}+ p_{\\lvert\\phi\\rangle\\_10}+ p_{\\lvert\\phi\\rangle\\_00}$，并使用基于梯度的优化方法。\n",
    "6. 重复 1-5，直到损失函数收敛。\n",
    "7. Charlie 在 $\\lvert\\psi\\rangle$ 和 $\\lvert\\phi\\rangle$ 两个之间随机选一个，并检验 Alice 和 Bob 的分辨方案是否可以正确地进行分辨。\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d09e6486",
   "metadata": {},
   "source": [
    "## Paddle Quantum 代码实现\n",
    "\n",
    "首先，我们导入相关的依赖包。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "5fc7fce4",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-09T04:10:28.022626Z",
     "start_time": "2021-03-09T04:10:23.716087Z"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from scipy.stats import unitary_group\n",
    "import paddle\n",
    "from paddle_quantum.locc import LoccNet"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5c5a100c",
   "metadata": {},
   "source": [
    "Charlie 需要随机生成两个正交态 $\\lvert\\psi\\rangle$ 以及 $\\lvert\\phi\\rangle$。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "9abe8856",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-09T04:10:38.723834Z",
     "start_time": "2021-03-09T04:10:38.718397Z"
    }
   },
   "outputs": [],
   "source": [
    "def states_orthogonal_random(n, num=2):\n",
    "    \n",
    "    # 随机生成两个正交态\n",
    "    assert num <= 2 ** n, \"return too many orthognal states\"\n",
    "    U = unitary_group.rvs(2 ** n)\n",
    "    return_list = []\n",
    "    for i in range(num):\n",
    "        return_list.append(U[i])\n",
    "    return return_list"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "724e5c48",
   "metadata": {},
   "source": [
    "下面是我们代码的主要部分，它定义了 Alice 和 Bob 的本地量子操作和损失函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "9cca865a",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-09T04:10:39.769534Z",
     "start_time": "2021-03-09T04:10:39.714589Z"
    }
   },
   "outputs": [],
   "source": [
    "class Net(LoccNet):\n",
    "    def __init__(self):\n",
    "        super(Net, self).__init__()\n",
    "        \n",
    "        # 添加第一个参与方 Alice\n",
    "        # 第一个参数 1 代表着 Alice 手里有几个量子比特\n",
    "        # 第二个参数代表着参与方的名字\n",
    "        self.add_new_party(1, party_name='Alice')\n",
    "        \n",
    "        # 添加第二个参与方 Bob\n",
    "        # 第一个参数 1 代表着 Bob 手里有几个量子比特\n",
    "        # 第二个参数代表着参与方的名字\n",
    "        self.add_new_party(1, party_name='Bob')\n",
    "        \n",
    "        # 初始化 Alice 的参数\n",
    "        self.theta1 = self.create_parameter(shape=[3],\n",
    "                                            default_initializer=paddle.nn.initializer.Uniform(low=0.0, high=2 * np.pi),\n",
    "                                            dtype=\"float64\")\n",
    "        # 初始化 Bob 的参数\n",
    "        # Bob 要准备两个电路来应对两个不同的测量结果\n",
    "        self.theta2 = self.create_parameter(shape=[3],\n",
    "                                            default_initializer=paddle.nn.initializer.Uniform(low=0.0, high=2 * np.pi),\n",
    "                                            dtype=\"float64\")\n",
    "        self.theta3 = self.create_parameter(shape=[3],\n",
    "                                            default_initializer=paddle.nn.initializer.Uniform(low=0.0, high=2 * np.pi),\n",
    "                                            dtype=\"float64\")\n",
    "        \n",
    "        # 将输入态写成密度矩阵形式\n",
    "        _states = states_orthogonal_random(2)\n",
    "        _states = [paddle.to_tensor(np.outer(init_state, init_state.conjugate())) for init_state in _states]\n",
    "        \n",
    "        # 初始化整个量子系统并分配量子态\n",
    "        self.set_init_state(_states[0], [('Alice', 0), ('Bob', 0)])\n",
    "        self.psi = self.init_status\n",
    "        self.phi = self.reset_state(self.init_status, _states[1], [('Alice', 0), ('Bob', 0)])\n",
    "\n",
    "    def A_circuit(self, theta, state, res):\n",
    "        # Alice 的电路\n",
    "        cir = self.create_ansatz('Alice')\n",
    "        # 添加单量子比特通用门\n",
    "        cir.u3(*theta, 0)\n",
    "        # 运行电路\n",
    "        after_state = cir.run(state)\n",
    "        # 测量电路，记录结果 \n",
    "        after_state = self.measure(status=after_state, which_qubits=('Alice', 0), results_desired=res)\n",
    "        return after_state\n",
    "\n",
    "    def B_circuit(self, theta, state, res):\n",
    "        # Bob 的电路\n",
    "        cir = self.create_ansatz('Bob')\n",
    "        # 添加单量子比特通用门\n",
    "        cir.u3(*theta, 0)\n",
    "        # 运行电路\n",
    "        after_state = cir.run(state)\n",
    "        # 测量电路，记录结果 \n",
    "        after_state = self.measure(status=after_state, which_qubits=('Bob', 0), results_desired=res)\n",
    "        return after_state\n",
    "    \n",
    "    def forward(self):\n",
    "        # 训练过程\n",
    "        # Alice 操作过后的量子态\n",
    "        phi = self.A_circuit(theta=self.theta1, state=self.phi, res=['0', '1'])\n",
    "        psi = self.A_circuit(theta=self.theta1, state=self.psi, res=['0', '1'])\n",
    "\n",
    "        # 定义损失函数\n",
    "        loss = 0\n",
    "        for each_phi in phi:\n",
    "            if each_phi.measured_result == '0':\n",
    "                # 我们要称之为 phi_1 ，代表着把 phi 预测为 psi\n",
    "                phi_1 = self.B_circuit(self.theta2, state=each_phi, res='1')\n",
    "                loss += phi_1.prob\n",
    "            elif each_phi.measured_result == '1':\n",
    "                psi_1 = self.B_circuit(self.theta3, state=each_phi, res='1')\n",
    "                loss += psi_1.prob\n",
    "\n",
    "        for each_psi in psi:\n",
    "            if each_psi.measured_result == '0':\n",
    "                phi_0 = self.B_circuit(self.theta2, state=each_psi, res='0')\n",
    "                loss += phi_0.prob\n",
    "            elif each_psi.measured_result == '1':\n",
    "                psi_0 = self.B_circuit(self.theta3, state=each_psi, res='0')\n",
    "                loss += psi_0.prob\n",
    "\n",
    "        return loss\n",
    "\n",
    "    def evaluate(self):\n",
    "        # 测试过程\n",
    "        choice = np.random.choice(['phi', 'psi'])\n",
    "        if choice == 'phi':\n",
    "            self.status = self.phi\n",
    "        else:\n",
    "            self.status = self.psi\n",
    "        print('Charlie 选择的态是', choice)\n",
    "\n",
    "        # Alice 的操作\n",
    "        status = self.A_circuit(theta=self.theta1, state=self.status, res=['0', '1'])\n",
    "        # Bob 的操作 \n",
    "        result_0 = list()\n",
    "        result_1 = list()\n",
    "        for each_status in status:\n",
    "            if each_status.measured_result == '0':\n",
    "                phi = self.B_circuit(theta=self.theta2, state=each_status, res=['0', '1'])\n",
    "                result_0.append(phi[0].prob.numpy())\n",
    "                result_0.append(phi[1].prob.numpy())\n",
    "\n",
    "            elif each_status.measured_result == '1':\n",
    "                psi = self.B_circuit(theta=self.theta3, state=each_status, res=['0', '1'])\n",
    "                result_1.append(psi[0].prob.numpy())\n",
    "                result_1.append(psi[1].prob.numpy())\n",
    "        print(\"Alice 和 Bob 将这个态分辨为 phi 的概率为:\", result_0[0][0] + result_1[0][0])\n",
    "        print(\"Alice 和 Bob 将这个态分辨为 psi 的概率为:\", result_0[1][0] + result_1[1][0])\n",
    "        "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1e648ea7",
   "metadata": {},
   "source": [
    "训练 Alice 和 Bob 的电路，然后随机选择两个正交态 $\\lvert\\psi\\rangle$ 和 $\\lvert\\phi\\rangle$ 之一以通过我们训练的电路，以检查它们是否可以区分。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "3c722a55",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-09T04:10:45.887729Z",
     "start_time": "2021-03-09T04:10:40.821447Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "itr 0: 0.8761168230331675\n",
      "itr 10: 0.20833627335270305\n",
      "itr 20: 0.09979993875586758\n",
      "itr 30: 0.05990778041973435\n",
      "itr 40: 0.02806489116318598\n",
      "itr 50: 0.026191291836946892\n",
      "itr 60: 0.02245618597383097\n",
      "itr 70: 0.01903560177140634\n",
      "itr 80: 0.016262801555360196\n",
      "itr 90: 0.011920717566034985\n",
      "最小损失: 0.0077142257760679424\n",
      "======================== 测试阶段 ===============================\n",
      "Charlie 选择的态是 psi\n",
      "Alice 和 Bob 将这个态分辨为 phi 的概率为: 0.003188464207077198\n",
      "Alice 和 Bob 将这个态分辨为 psi 的概率为: 0.9968115357929229\n",
      "Charlie 选择的态是 phi\n",
      "Alice 和 Bob 将这个态分辨为 phi 的概率为: 0.99590992116251\n",
      "Alice 和 Bob 将这个态分辨为 psi 的概率为: 0.004090078837489469\n"
     ]
    }
   ],
   "source": [
    "ITR = 100  # 设置训练步数\n",
    "LR = 0.1   # 设置学习速率\n",
    "SEED = 999 # 固定 PQC 中参数的随机种子\n",
    "np.random.seed(SEED)\n",
    "paddle.seed(SEED)\n",
    "\n",
    "net = Net()\n",
    "opt = paddle.optimizer.Adam(learning_rate=LR, parameters=net.parameters())\n",
    "loss_list = list()\n",
    "# 通过梯度下降训练 LOCC 网络以进行 ITR 次迭代\n",
    "for itr in range(ITR):\n",
    "    loss = net()\n",
    "    loss.backward()\n",
    "    opt.minimize(loss)\n",
    "    opt.clear_grad()\n",
    "    loss_list.append(loss.numpy()[0])\n",
    "    if itr % 10 == 0:\n",
    "        print(\"itr \" + str(itr) + \":\", loss.numpy()[0])\n",
    "print(\"最小损失:\", loss_list[-1])\n",
    "\n",
    "print(\"======================== 测试阶段 ===============================\")\n",
    "np.random.seed(10)\n",
    "net.evaluate()\n",
    "np.random.seed(6)\n",
    "net.evaluate()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32c38151",
   "metadata": {},
   "source": [
    "## 结论\n",
    "\n",
    "从模拟结果可以看出，经过训练的量子电路可以准确地分辨出两个正交的量子态，精确度 $>99.9\\%$。这里有一个值得思考的问题：我们是否可以通过添加更多态来推广这种分辨方案？"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "249db983",
   "metadata": {},
   "source": [
    "---\n",
    "## 参考文献\n",
    "\n",
    "[1] Barnett, Stephen M., and Sarah Croke. \"Quantum state discrimination.\" [Advances in Optics and Photonics 1.2 (2009): 238-278.](https://www.osapublishing.org/abstract.cfm?id=176580)\n",
    "\n",
    "[2] Chefles, Anthony. \"Quantum state discrimination.\" [Contemporary Physics 41.6 (2000): 401-424.](https://arxiv.org/abs/quant-ph/0010114)\n",
    "\n",
    "[3] Walgate, Jonathan, et al. \"Local distinguishability of multipartite orthogonal quantum states.\" [Physical Review Letters 85.23 (2000): 4972.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.85.4972)\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
